package com.gzzyy.unionchain.util

import android.util.Base64
import android.util.Log
import com.alibaba.fastjson.JSONObject
import com.blankj.utilcode.util.EncryptUtils
import com.gzzyy.unionchain.App
import com.gzzyy.unionchain.communicate.Action
import com.gzzyy.unionchain.data.ObjectBox
import com.gzzyy.unionchain.data.bean.*
import io.objectbox.Box
import java.security.*
import java.security.interfaces.RSAPrivateKey
import java.security.spec.PKCS8EncodedKeySpec
import javax.crypto.BadPaddingException
import javax.crypto.Cipher
import javax.crypto.IllegalBlockSizeException
import javax.crypto.NoSuchPaddingException

object KeyUtil {

    fun getTransferHash(preHash: String, nonce: String, createTime: String): String {
        return EncryptUtils.encryptSHA256ToString(preHash + nonce + createTime).toLowerCase()
    }

    fun getTransferHashSign(hash: String): String {
        val priKeyBytes = Base64.decode(App.tripleKey?.priKey, Base64.NO_WRAP)
        return privateKeyRSAEncrypt(priKeyBytes, hash)
    }

    fun generateUserKey(safeUserKey: String): Pair<User, TripleKey> {
        val keyPair = generateRSAKeyPair()
        val priKey = encode2String(keyPair?.private?.encoded)
        val pubEncode = keyPair?.public?.encoded
        val pubKey = encode2String(pubEncode)
        val pubHash = EncryptUtils.encryptSHA256ToString(pubKey)
        val walletAddress = pubHash.substring(pubHash.length - 40, pubHash.length).toLowerCase()
        val tripleKeyBox = ObjectBox.boxStore.boxFor(
            TripleKey::class.java
        )
        tripleKeyBox.removeAll()
        val tripleKey = TripleKey(walletAddress, priKey, pubKey)
        tripleKeyBox.put(tripleKey)
        val userBox = ObjectBox.boxStore.boxFor(User::class.java)
        userBox.removeAll()
        val user =
            User(walletAddress, safeUserKey)
        userBox.put(user)
        Log.e("test", "pri = $priKey")
        Log.e("test", "pub = $pubKey")
        return Pair(user, tripleKey)
    }

    private fun generateRSAKeyPair(keyLength: Int = 2048): KeyPair? {
        return try {
            val kpg: KeyPairGenerator = KeyPairGenerator.getInstance("RSA")
            kpg.initialize(keyLength)
            kpg.genKeyPair()
        } catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
            null
        }
    }

    fun encode2String(input: ByteArray?): String {
        return String(Base64.encode(input, Base64.NO_WRAP))
    }

    fun getTripleKey(walletAddress: String?): TripleKey? {
        val tripleKeyBox = ObjectBox.boxStore.boxFor(TripleKey::class.java)
        val queryBuilder = tripleKeyBox.query()
        val query = queryBuilder.equal(TripleKey_.walletAddress, walletAddress).build()
        return query.findFirst()
    }

    fun privateKeyRSAEncrypt(key: ByteArray?, input: String): String {
        val keySpec = PKCS8EncodedKeySpec(key)
        val priKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec)
        val cipher: Cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
        cipher.init(Cipher.ENCRYPT_MODE, priKey)
        val cipherText: ByteArray = cipher.doFinal(input.toByteArray())
        return String(Base64.encode(cipherText, Base64.NO_WRAP))
        //加密后的内容
        Log.e("test", "******************以上是加密部分cipher: " + String(cipherText))
    }

    @Throws(Exception::class)
    fun encrypt(
        privateKey: RSAPrivateKey?,
        plainTextData: ByteArray?
    ): ByteArray? {
        if (privateKey == null) {
            throw Exception("加密私钥为空, 请设置")
        }
        var cipher: Cipher? = null
        return try {
            // 使用默认RSA
            cipher = Cipher.getInstance("RSA")
            cipher.init(Cipher.ENCRYPT_MODE, privateKey)
            cipher.doFinal(plainTextData)
        } catch (e: NoSuchAlgorithmException) {
            throw Exception("无此加密算法")
        } catch (e: NoSuchPaddingException) {
            e.printStackTrace()
            null
        } catch (e: InvalidKeyException) {
            throw Exception("加密私钥非法,请检查")
        } catch (e: IllegalBlockSizeException) {
            throw Exception("明文长度非法")
        } catch (e: BadPaddingException) {
            throw Exception("明文数据已损坏")
        }
    }


    fun findBalance(walletAddress: String?): Float {
        var result = -1f
        val transferBeanBox = ObjectBox.getRecordBox()
        val queryBuilder = transferBeanBox.query()
        val transferBean = queryBuilder.equal(TransactionRecord_.from, walletAddress)
            .or().equal(TransactionRecord_.to, walletAddress)
            .orderDesc(TransactionRecord_.height)
            .build().findFirst()
        if (transferBean != null) {
            if (transferBean.from == walletAddress) {
                result = transferBean.from_utxo
            } else if (transferBean.to == walletAddress) {
                result = transferBean.to_utxo
            }
        }
        return result
    }

    fun combineTransferBean(
        from: String?,
        to: String?,
        money: Float,
        from_utxo: Float,
        to_utxo: Float,
        nonce: String,
        prevHash: String,
        height: Int,
        hash: String,
        hash_sign: String,
        create_time: String
    ): JSONObject {
        val jsonObj = JSONObject()
        jsonObj["action"] = Action.Transfer.value
        val dataJsonObj = JSONObject()
        dataJsonObj["from"] = from
        dataJsonObj["to"] = to
        dataJsonObj["money"] = money
        dataJsonObj["from_utxo"] = from_utxo
        dataJsonObj["to_utxo"] = to_utxo
        dataJsonObj["nonce"] = nonce
        dataJsonObj["prevhash"] = prevHash
        dataJsonObj["height"] = height
        dataJsonObj["hash"] = hash
        dataJsonObj["hash_sign"] = hash_sign
        dataJsonObj["create_time"] = create_time
        dataJsonObj["public_key"] = App.tripleKey?.pubKey
        jsonObj["data"] = dataJsonObj
        return jsonObj
    }

    fun findPreHash(recordBox: Box<TransactionRecord>): String {
        val transferBean =
            recordBox.query().orderDesc(TransactionRecord_.height).build().findFirst()
        if (transferBean != null) {
            return transferBean.hash
        }
        return ""
    }

}